using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Data;

namespace Axe.Data
{
    internal delegate bool MappingObjDelegate(SqlDataReader reader, object item);
    public delegate bool MappingDelegate<T>(SqlDataReader reader, T item);
    public delegate bool MappingDelegateRef<T>(SqlDataReader reader,ref T item);

    public delegate T ActivatorDelegate<T>();

    public static class SqlMappingPattern
    {
        static T InternalActivator<T>()
        {
            return Activator.CreateInstance<T>();
        }

        public static bool Map<T>(
            SqlCommand cmd, 
            MappingDelegate<T> mapDelegate, 
            ICollection<T> targetCollection)
        {
            return Map(cmd,mapDelegate,targetCollection,InternalActivator<T>);
        } 

        public static bool Map<T>(SqlCommand cmd, 
            MappingDelegate<T> mapDelegate, 
            ICollection<T> targetCollection, 
            SqlPaging paging)
        {
            return Map(cmd,mapDelegate,targetCollection,InternalActivator<T>, paging);
        }

        #region public static bool Map<T>(SqlCommand cmd, MappingDelegate<T> mapDelegate, ICollection<T> targetCollection, ActivatorDelegate<T> activator)
        public static bool Map<T>(SqlCommand cmd, MappingDelegate<T> mapDelegate, ICollection<T> targetCollection, ActivatorDelegate<T> activator)
        {
            using (SqlDataReader reader = cmd.ExecuteReader())
            {
                while (reader.HasRows && reader.Read())
                {
                    T item = activator();

                    if (Map<T>(reader, item, mapDelegate))
                        targetCollection.Add(item);
                    else
                        return false;
                }
            }

            return true;
        }
        #endregion

        #region public static bool Map<T>(SqlCommand cmd, MappingDelegate<T> mapDelegate, ICollection<T> targetCollection, ActivatorDelegate<T> activator, SqlPaging paging)
        public static bool Map<T>(SqlCommand cmd, MappingDelegate<T> mapDelegate, ICollection<T> targetCollection, ActivatorDelegate<T> activator, SqlPaging paging)
        {
            bool success = true;
            using (SqlDataReader reader = cmd.ExecuteReader())
            {
                paging.Execute(reader, delegate (SqlDataReader r) 
                {
                    T item = activator();

                    if (Map<T>(r, item, mapDelegate))
                        targetCollection.Add(item);
                    else
                        success = false;                
                });
            }

            return success;
        }
        #endregion

        public static bool Map<T>(SqlCommand cmd, MappingDelegateRef<T> mapDelegate, ICollection<T> targetCollection)
        {
            return Map(
                cmd,
                mapDelegate,
                targetCollection,
                InternalActivator<T>);
        }

        public static bool Map<T>(SqlCommand cmd, MappingDelegateRef<T> mapDelegate, ICollection<T> targetCollection,SqlPaging paging)
        {
            return Map(cmd,mapDelegate,targetCollection,InternalActivator<T>, paging);
        } 

        #region public static bool Map<T>(SqlCommand cmd, MappingDelegateRef<T> mapDelegate, ICollection<T> targetCollection, ActivatorDelegate<T> activator)
        public static bool Map<T>(SqlCommand cmd, MappingDelegateRef<T> mapDelegate, ICollection<T> targetCollection, ActivatorDelegate<T> activator)
        {
            using (SqlDataReader reader = cmd.ExecuteReader())
            {
                while (reader.HasRows && reader.Read())
                {
                    T item = activator();

                    if (Map<T>(reader, ref item, mapDelegate))
                        targetCollection.Add(item);
                    else
                        return false;
                }
            }

            return true;
        } 
        #endregion

        #region public static bool Map<T>(SqlCommand cmd, MappingDelegateRef<T> mapDelegate, ICollection<T> targetCollection, ActivatorDelegate<T> activator,SqlPaging paging)
        public static bool Map<T>(SqlCommand cmd, MappingDelegateRef<T> mapDelegate, ICollection<T> targetCollection, ActivatorDelegate<T> activator,SqlPaging paging)
        {
            bool success = true;
            using (SqlDataReader reader = cmd.ExecuteReader())
            {
                paging.Execute(reader, delegate(SqlDataReader r)
                {
                    T item = activator();

                    if (Map<T>(r, ref item, mapDelegate))
                        targetCollection.Add(item);
                    else
                        success = false;
                });
            }

            return success;
        }
        #endregion

        #region public static bool Map<T>(SqlDataReader reader, T item, MappingDelegate<T> mapDelegate)
        public static bool Map<T>(SqlDataReader reader, T item, MappingDelegate<T> mapDelegate)
        {
            return mapDelegate(reader, item);
        } 
        #endregion

        #region public static bool Map<T>(SqlDataReader reader, ref T item, MappingDelegateRef<T> mapDelegate)
        public static bool Map<T>(SqlDataReader reader, ref T item, MappingDelegateRef<T> mapDelegate)
        {
            return mapDelegate(reader, ref item);
        } 
        #endregion

        internal static bool MapSingleRowObj(SqlCommand cmd, MappingObjDelegate mapDelegate, object item)
        {
            using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow))
            {
                if(reader.HasRows && reader.Read())
                {
                    if (Map(reader, item, mapDelegate))
                        return true;
                    else
                        return false;
                }

                return false;
            }
        }

        private static bool Map(SqlDataReader reader, object item, MappingObjDelegate mapDelegate)
        {
            return mapDelegate(reader, item);
        }

        public static bool MapSingleRow<T>(SqlCommand cmd, MappingDelegate<T> mapDelegate, T item)
        {
            using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow))
            {
                if(reader.HasRows && reader.Read())
                {
                    if (Map<T>(reader, item, mapDelegate))
                        return true;
                    else
                        return false;
                }

                return false;
            }
        }

        public static bool MapSingleRow<T>(SqlCommand cmd, MappingDelegateRef<T> mapDelegate, ref T item)
        {
            using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow))
            {
                if (reader.HasRows && reader.Read())
                {
                    if (Map<T>(reader, ref item, mapDelegate))
                        return true;
                    else
                        return false;
                }

                return false;
            }
        }

        public static class Activators
        {
            public static string StringActivator()
            {
                return String.Copy(String.Empty);
            }
        }
    }
}
